#region Using Directives and Copyright Notice

// Copyright (c) 2007-2010, Computer Consultancy Pty Ltd
// All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of the Computer Consultancy Pty Ltd nor the
//       names of its contributors may be used to endorse or promote products
//       derived from this software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
// ARE DISCLAIMED. IN NO EVENT SHALL COMPUTER CONSULTANCY PTY LTD BE LIABLE 
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
// DAMAGE.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;

#endregion

namespace Interlace.Collections
{
    public enum BucketLumpType
    {
        IncludeItemsLessThanComparison = 1,
        IncludeItemsGreaterThanComparison = 2
    }

    public class BucketSet<T> : IEnumerable<Bucket<T>>
    {
        string _propertyName;
        List<Bucket<T>> _list;
        bool _hasRemainderBucket = false;

        BucketLumpType _lumpType;

        public BucketSet(string propertyName, BucketLumpType lumpType)
        {
            _list = new List<Bucket<T>>();
            _propertyName = propertyName;
            _lumpType = lumpType;
        }

        public void AddBucket(IComparable comparisonObject, object tag)
        {
            Bucket<T> bucket = new Bucket<T>(_propertyName, comparisonObject, _lumpType);
            bucket.Tag = tag;

            _list.Add(bucket);
        }

        public void AddBucket(IComparable comparisonObject)
        {
            _list.Add(new Bucket<T>(_propertyName, comparisonObject, _lumpType));
        }

        public void AddRemainderBucket(object tag)
        {
            if (_hasRemainderBucket) throw new InvalidOperationException("This bucket set already has a remainder bucket.");

            RemainderBucket<T> bucket = new RemainderBucket<T>();
            bucket.Tag = tag;

            _list.Add(bucket);
            _hasRemainderBucket = true;
        }

        public void AddRemainderBucket()
        {
            if (_hasRemainderBucket) throw new InvalidOperationException("This bucket set already has a remainder bucket.");

            _list.Add(new RemainderBucket<T>());
            _hasRemainderBucket = true;
        }

        public void FillBuckets(IEnumerable<T> items)
        {
            _list.Sort();

            foreach(T item in items)
            {
                foreach(Bucket<T> bucket in _list)
                {
                    if (bucket.ItemFitsInBucket(item))
                    {
                        bucket.AddItemToBucket(item);
                        break;
                    }
                }
            }
        }

        public List<Bucket<T>> Buckets
        {
            get { return _list; }
        }

        #region IEnumerable<Bucket<T>> Members

        public IEnumerator<Bucket<T>> GetEnumerator()
        {
            return _list.GetEnumerator();
        }

        #endregion

        #region IEnumerable Members

        IEnumerator IEnumerable.GetEnumerator()
        {
            return _list.GetEnumerator();
        }

        #endregion

        public Bucket<T> this[int index]
        {
            get { return _list[index]; }
        }

        public void ClearItems()
        {
            foreach(Bucket<T> bucket in _list)
            {
                bucket.Items.Clear();
            }
        }
    }
}
